home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 11 / Mac Magazin and MacEasy Magazine CD - Issue 11.iso / Sharewarebibliothek / Entwickler / WASTE 1.1b1 Distribution / WASTE Source / WEDrawing.p < prev    next >
Text File  |  1995-06-01  |  33KB  |  1,079 lines

  1. unit WEDrawing;
  2.  
  3. { WASTE PROJECT: }
  4. { Drawing routines and other basic support functions }
  5.  
  6. { Copyright © 1993-1995 Marco Piovanelli }
  7. { All Rights Reserved }
  8.  
  9. interface
  10.     uses
  11.         WEObjects;
  12.  
  13.     function WEOffsetToLine (offset: LongInt;
  14.                                     hWE: WEHandle): LongInt;
  15.     function _WEPixelToLine (vOffset: LongInt;
  16.                                     hWE: WEHandle): LongInt;
  17.     function _WEOffsetToRun (offset: LongInt;
  18.                                     hWE: WEHandle): LongInt;
  19.     procedure _WEGetIndStyle (runIndex: LongInt;
  20.                                     var info: WERunInfo;
  21.                                     hWE: WEHandle);
  22.     procedure WEGetRunInfo (offset: LongInt;
  23.                                     var info: WERunInfo;
  24.                                     hWE: WEHandle);
  25.     function WEGetSelectedObject (var hObjectDesc: WEObjectDescHandle;
  26.                                     hWE: WEHandle): OSErr;
  27.     function WEFindNextObject (offset: LongInt;
  28.                                     var hObjectDesc: WEObjectDescHandle;
  29.                                     hWE: WEHandle): LongInt;
  30.     procedure _WEContinuousStyleRange (rangeStart, rangeEnd: LongInt;
  31.                                     var mode: Integer;
  32.                                     var ts: WETextStyle;
  33.                                     hWE: WEHandle);
  34.     procedure _WESynchNullStyle (hWE: WEHandle);
  35.     function WEContinuousStyle (var mode: Integer;
  36.                                     var ts: WETextStyle;
  37.                                     hWE: WEHandle): Boolean;
  38.     procedure _WESegmentLoop (firstLine, lastLine: LongInt;
  39.                                     function Callback (pLine: LinePtr;
  40.                                                                     pAttrs: WERunAttributesPtr;
  41.                                                                     pSegment: Ptr;
  42.                                                                     segmentStart, segmentLength: LongInt;
  43.                                                                     styleRunPosition: JustStyleCode): Boolean;
  44.                                     hWE: WEHandle);
  45.     procedure _WEDrawLines (firstLine, lastLine: LongInt;
  46.                                     doErase: Boolean;
  47.                                     hWE: WEHandle);
  48.     function _WECalcPenIndent (slop: Integer;
  49.                                     alignment: Integer): Integer;
  50.     procedure _WESaveQDEnvironment (port: GrafPtr;
  51.                                     saveColor: Boolean;
  52.                                     var theEnvironment: QDEnvironment);
  53.     procedure _WERestoreQDEnvironment (var theEnvironment: QDEnvironment);
  54.     procedure _WEFillFontInfo (port: GrafPtr;
  55.                                     var targetStyle: WERunAttributes);
  56.     procedure _WECopyStyle (var sourceStyle, targetStyle: WETextStyle;
  57.                                     offStyles: Integer;
  58.                                     mode: Integer);
  59.     function _WEOffsetInRange (offset: LongInt;
  60.                                     edge: SignedByte;
  61.                                     rangeStart, rangeEnd: LongInt): Boolean;
  62.  
  63. implementation
  64.     uses
  65.         Palettes, QDOffscreen, ToolUtils;
  66.  
  67. { If WASTE_RESOLVE_FONT_DESIGNATORS is TRUE, font designators (the special }
  68. { IDs 0 and 1 that identify the system and the application font, respectively) }
  69. { are mapped by _WECopyStyle to the actual font IDs }
  70.  
  71. {$SETC WASTE_RESOLVE_FONT_DESIGNATORS = TRUE}
  72.  
  73.     function WEOffsetToLine (offset: LongInt;
  74.                                     hWE: WEHandle): LongInt;
  75.  
  76. { given a byte offset into the text, find the corresponding line index }
  77.  
  78.         var
  79.             pWE: WEPtr;
  80.             pLines: LineArrayPtr;
  81.             minIndex, maxIndex, index: LongInt;
  82.     begin
  83.         pWE := hWE^;
  84.  
  85. { get a pointer to the line array }
  86.         pLines := pWE^.hLines^;
  87.  
  88. { do a fast binary search through the style run array }
  89.         minIndex := 0;
  90.         maxIndex := pWE^.nLines;
  91.  
  92.         while (minIndex < maxIndex) do
  93.             begin
  94.                 index := BSR(minIndex + maxIndex, 1);
  95.                 if (offset >= pLines^[index].lineStart) then
  96.                     if (offset < pLines^[index + 1].lineStart) then
  97.                         Leave
  98.                     else
  99.                         minIndex := index + 1
  100.                 else
  101.                     maxIndex := index;
  102.             end;  { while }
  103.  
  104.         WEOffsetToLine := index;
  105.  
  106.     end;  { WEOffsetToLine }
  107.  
  108.     function _WEPixelToLine (vOffset: LongInt;
  109.                                     hWE: WEHandle): LongInt;
  110.  
  111. { given a vertical pixel offset in local coordinates, }
  112. { find the corresponding line index }
  113.  
  114.         var
  115.             pWE: WEPtr;
  116.             pLines: LineArrayPtr;
  117.             minIndex, maxIndex, index: LongInt;
  118.     begin
  119.         pWE := hWE^;
  120.  
  121. { get a pointer to the line array }
  122.         pLines := pWE^.hLines^;
  123.  
  124. { do a fast binary search through the style run array }
  125.         minIndex := 0;
  126.         maxIndex := pWE^.nLines;
  127.  
  128.         while (minIndex < maxIndex) do
  129.             begin
  130.                 index := BSR(minIndex + maxIndex, 1);
  131.                 if (vOffset >= pLines^[index].lineOrigin) then
  132.                     if (vOffset < pLines^[index + 1].lineOrigin) then
  133.                         Leave
  134.                     else
  135.                         minIndex := index + 1
  136.                 else
  137.                     maxIndex := index;
  138.             end;  { while }
  139.  
  140.         _WEPixelToLine := index;
  141.  
  142.     end;  { _WEPixelToLine }
  143.  
  144.     function _WEOffsetToRun (offset: LongInt;
  145.                                     hWE: WEHandle): LongInt;
  146.         var
  147.             pWE: WEPtr;
  148.             pRuns: RunArrayPtr;
  149.             minIndex, maxIndex, index: LongInt;
  150.     begin
  151.         _WEOffsetToRun := 0;
  152.         pWE := hWE^;
  153.  
  154. { get a pointer to the style run array }
  155.         pRuns := pWE^.hRuns^;
  156.  
  157. { do a fast binary search through the style run array }
  158.         minIndex := 0;
  159.         maxIndex := pWE^.nRuns;
  160.  
  161.         while (minIndex < maxIndex) do
  162.             begin
  163.                 index := BSR(minIndex + maxIndex, 1);
  164.                 if (offset >= pRuns^[index].runStart) then
  165.                     if (offset < pRuns^[index + 1].runStart) then
  166.                         Leave
  167.                     else
  168.                         minIndex := index + 1
  169.                 else
  170.                     maxIndex := index;
  171.             end;  { while }
  172.  
  173.         _WEOffsetToRun := index;
  174.  
  175.     end;  { _WEOffsetToRun }
  176.  
  177.     procedure _WEGetIndStyle (runIndex: LongInt;
  178.                                     var info: WERunInfo;
  179.                                     hWE: WEHandle);
  180.         var
  181.             pWE: WEPtr;
  182.             pTheRun: RunArrayPeek;
  183.     begin
  184.         pWE := hWE^;
  185.  
  186. { get a pointer to the specified run array element }
  187.         pTheRun := @pWE^.hRuns^^[runIndex];
  188.  
  189. { fill in the runStart and runEnd fields from the style run array }
  190.         info.runStart := pTheRun^.first.runStart;
  191.         info.runEnd := pTheRun^.second.runStart;
  192.  
  193. { copy the style information from the appropriate entry in the style table }
  194.         info.runAttrs := pWE^.hStyles^^[pTheRun^.first.styleIndex].info;
  195.  
  196.     end;  { _WEGetIndStyle }
  197.  
  198.     procedure WEGetRunInfo (offset: LongInt;
  199.                                     var info: WERunInfo;
  200.                                     hWE: WEHandle);
  201.     begin
  202.         _WEGetIndStyle(_WEOffsetToRun(offset, hWE), info, hWE);
  203.     end;  { WEGetRunInfo }
  204.  
  205.     function WEGetSelectedObject (var hObjectDesc: WEObjectDescHandle;
  206.                                     hWE: WEHandle): OSErr;
  207.         var
  208.             pWE: WEPtr;
  209.             runInfo: WERunInfo;
  210.     begin
  211.  
  212. { assume current selection is not an embedded object }
  213.         WEGetSelectedObject := weObjectNotFoundErr;
  214.         hObjectDesc := nil;
  215.  
  216. { check selection range }
  217.         pWE := hWE^;
  218.         if (pWE^.selEnd - pWE^.selStart = 1) then
  219.             begin
  220.  
  221. { check run info }
  222.                 WEGetRunInfo(pWE^.selStart, runInfo, hWE);
  223.                 hObjectDesc := WEObjectDescHandle(runInfo.runAttrs.runStyle.tsObject);
  224.                 if (hObjectDesc <> nil) then
  225.                     WEGetSelectedObject := noErr;
  226.             end;
  227.     end;  { WEGetSelectedObject }
  228.  
  229.     function WEFindNextObject (offset: LongInt;
  230.                                     var hObjectDesc: WEObjectDescHandle;
  231.                                     hWE: WEHandle): LongInt;
  232.         var
  233.             pStyles: StyleTablePtr;
  234.             pRun: RunArrayElementPtr;
  235.             obj: LongInt;
  236.     begin
  237.         WEFindNextObject := kInvalidOffset;
  238.         obj := kNullObject;
  239.         pStyles := hWE^^.hStyles^;
  240.  
  241. { get a pointer to the run array element immediately following offset }
  242.         pRun := @hWE^^.hRuns^^[_WEOffsetToRun(offset + 1, hWE)];
  243.  
  244. { perform a fast linear scan of the run array looking for a run whose }
  245. { corresponding style table entry points to an embedded object; }
  246. { the search will stop anyway because the last run array element has styleIndex = -1 }
  247.         while (pRun^.styleIndex >= 0) do
  248.             begin
  249.                 obj := pStyles^[pRun^.styleIndex].info.runStyle.tsObject;
  250.                 if (obj <> kNullObject) then
  251.                     begin
  252.                         WEFindNextObject := pRun^.runStart;
  253.                         Leave;    { enclosing while }
  254.                     end;
  255.                 pRun := RunArrayElementPtr(LongInt(pRun) + SizeOf(RunArrayElement));
  256.             end;  { while }
  257.         hObjectDesc := WEObjectDescHandle(obj);
  258.     end;  { WEFindNextObject }
  259.  
  260.     procedure _WEContinuousStyleRange (rangeStart, rangeEnd: LongInt;
  261.                                     var mode: Integer;
  262.                                     var ts: WETextStyle;
  263.                                     hWE: WEHandle);
  264.  
  265. { find out which style attributes are continous over the specified text range }
  266. { on entry, the mode bitmap specifies which attributes are to be checked }
  267. { on exit, the mode bitmap specifies the continuous attributes, also copied to ts }
  268.  
  269.         var
  270.             pWE: WEPtr;
  271.             bitmap: LongInt;
  272.             runIndex: LongInt;
  273.             runInfo: WERunInfo;
  274.     begin
  275.         pWE := hWE^;
  276.  
  277. { get bitmap of style attributes to check (masking out private and unused bits) }
  278.         bitmap := BAND(mode, weDoAll);
  279.  
  280. { get style info at the beginning of the specified range }
  281.         runIndex := _WEOffsetToRun(rangeStart, hWE);
  282.         _WEGetIndStyle(runIndex, runInfo, hWE);
  283.  
  284. { copy the specified fields to ts }
  285.         _WECopyStyle(runInfo.runAttrs.runStyle, ts, 0, BOR(bitmap, weDoReplaceFace));
  286.  
  287. { loop through style runs across the current selection range }
  288. { if we determine that all specified attributes are discontinuous, we exit prematurely }
  289.         repeat
  290.             _WEGetIndStyle(runIndex, runInfo, hWE);
  291.  
  292. { determine which attributes have changed, if any }
  293.             if BTST(bitmap, kModeFont) then
  294.                 if (runInfo.runAttrs.runStyle.tsFont <> ts.tsFont) then
  295.                     BCLR(bitmap, kModeFont);
  296.  
  297.             if BTST(bitmap, kModeFace) then
  298.                 if (runInfo.runAttrs.runStyle.tsFace <> ts.tsFace) then
  299.                     begin
  300.                         ts.tsFace := BAND(ts.tsFace, runInfo.runAttrs.runStyle.tsFace);
  301.                         if (ts.tsFace = 0) then
  302.                             BCLR(bitmap, kModeFace);
  303.                     end;
  304.  
  305.             if BTST(bitmap, kModeSize) then
  306.                 if (runInfo.runAttrs.runStyle.tsSize <> ts.tsSize) then
  307.                     BCLR(bitmap, kModeSize);
  308.  
  309.             if BTST(bitmap, kModeColor) then
  310.                 if (not _WEBlockCmp(@runInfo.runAttrs.runStyle.tsColor, @ts.tsColor, SizeOf(RGBColor))) then
  311.                     BCLR(bitmap, kModeColor);
  312.  
  313.             runIndex := runIndex + 1;
  314.         until (bitmap = 0) | (runInfo.runEnd >= rangeEnd);
  315.  
  316.         mode := bitmap;
  317.     end;  { _WEContinuousStyleRange }
  318.  
  319.     procedure _WESynchNullStyle (hWE: WEHandle);
  320.  
  321. { This routine fills the nullStyle field of the WE record with valid information }
  322. { and makes sure that the null style font belongs to the keyboard script. }
  323.  
  324.         var
  325.             pWE: WEPtr;
  326.             runIndex: LongInt;
  327.             keyboardScript: ScriptCode;
  328.             fontID: Integer;
  329.             runInfo: WERunInfo;
  330.     begin
  331.         pWE := hWE^;        { the WE record must be already locked }
  332.  
  333. { find the run index of the style run preceding the insertion point }
  334.         runIndex := _WEOffsetToRun(pWE^.selStart - 1, hWE);
  335.  
  336. { if the nullStyle record is marked as invalid, fill it with the style attributes }
  337. { associated with the character preceding the insertion point, and mark it as valid }
  338.         if (not BTST(pWE^.flags, weFUseNullStyle)) then
  339.             begin
  340.                 _WEGetIndStyle(runIndex, runInfo, hWE);
  341.                 pWE^.nullStyle := runInfo.runAttrs;
  342.                 BSET(pWE^.flags, weFUseNullStyle);
  343.             end;
  344.  
  345. { if only the Roman script is installed, we're finished }
  346.         if (not BTST(pWE^.flags, weFNonRoman)) then
  347.             Exit(_WESynchNullStyle);
  348.  
  349. { *** FONT / KEYBOARD SYNCHRONIZATION *** }
  350. { get the keyboard script }
  351.         keyboardScript := GetScriptManagerVariable(smKeyScript);
  352.  
  353. { find out what font will be used for the next character typed }
  354.         fontID := pWE^.nullStyle.runStyle.tsFont;
  355.  
  356. { do nothing if the font script is the same as the keyboard script }
  357.         if (FontToScript(fontID) = keyboardScript) then
  358.             Exit(_WESynchNullStyle);
  359.  
  360. { scan style runs starting from the insertion point backwards,}
  361. { looking for the first font belonging to the keyboard script }
  362.         repeat
  363.             _WEGetIndStyle(runIndex, runInfo, hWE);
  364.             fontID := runInfo.runAttrs.runStyle.tsFont;
  365.             if (FontToScript(fontID) = keyboardScript) then
  366.                 Leave;
  367.             runIndex := runIndex - 1;
  368.         until (runIndex < 0);
  369.  
  370. { if no font was ever used for the keyboard script, default to the }
  371. { application font for the script }
  372.         if (runIndex < 0) then
  373.             fontID := GetScriptVariable(keyboardScript, smScriptAppFond);
  374.  
  375. { change the font in the null style record }
  376.         pWE^.nullStyle.runStyle.tsFont := fontID;
  377.  
  378.     end;  { _WESynchNullStyle }
  379.  
  380.     function WEContinuousStyle (var mode: Integer;
  381.                                     var ts: WETextStyle;
  382.                                     hWE: WEHandle): Boolean;
  383.  
  384. { find out which style attributes are continous over the selection range }
  385. { on entry, the mode bitmap specifies which attributes are to be checked }
  386. { on exit, the mode bitmap specifies the continuous attributes, also copied to ts }
  387. { return TRUE if all specified attributes are continuous }
  388.  
  389.         var
  390.             pWE: WEPtr;
  391.             oldMode: Integer;
  392.             saveWELock: Boolean;
  393.     begin
  394.  
  395. { lock the WE record }
  396.         saveWELock := _WESetHandleLock(hWE, true);
  397.         pWE := hWE^;
  398.  
  399. { mask out private and unused bits in mode so we don't run the risk of overwriting }
  400. { memory past the ts record (which is defined as TextStyle in the public interfaces) }
  401.         mode := BAND(mode, weDoAll);
  402.  
  403. { two rather different paths are taken depending on whether }
  404. { the selection range is empty or not }
  405.         if (pWE^.selStart = pWE^.selEnd) then
  406.             begin
  407.  
  408. { if the selection range is empty, always return TRUE and set ts }
  409. { from the nullStyle record, after having validated it }
  410.                 WEContinuousStyle := true;
  411.                 _WESynchNullStyle(hWE);
  412.                 _WECopyStyle(pWE^.nullStyle.runStyle, ts, 0, BOR(mode, weDoReplaceFace));
  413.             end
  414.         else
  415.             begin
  416.  
  417. { otherwise get the continuous style attributes over the selection range }
  418.                 oldMode := mode;
  419.                 _WEContinuousStyleRange(pWE^.selStart, pWE^.selEnd, mode, ts, hWE);
  420.  
  421. { return TRUE if mode hasn't changed }
  422.                 WEContinuousStyle := (oldMode = mode);
  423.  
  424.             end;
  425.  
  426. { unlock the WE record }
  427.         if (_WESetHandleLock(hWE, saveWELock)) then
  428.             ;
  429.  
  430.     end;  { WEContinuousStyle }
  431.  
  432.     procedure _WESegmentLoop (firstLine, lastLine: LongInt;
  433.                                     function Callback (pLine: LinePtr;
  434.                                                                     pAttrs: WERunAttributesPtr;
  435.                                                                     pSegment: Ptr;
  436.                                                                     segmentStart, segmentLength: LongInt;
  437.                                                                     styleRunPosition: JustStyleCode): Boolean;
  438.                                     hWE: WEHandle);
  439.  
  440. { For each style segment on every line in the specified range, set up }
  441. { text attributes in the port and call the callback. }
  442. { the WE record must be already locked }
  443.  
  444.         var
  445.             pWE: WEPtr;
  446.             pLines: LineArrayPtr;
  447.             pText: LongInt;
  448.             lineIndex: LongInt;
  449.             runIndex, previousRunIndex: LongInt;
  450.             lineStart, lineEnd, segmentStart, segmentEnd: LongInt;
  451.             styleRunPosition: JustStyleCode;
  452.             runInfo: WERunInfo;
  453.             saveLineLock: Boolean;
  454.             saveTextLock: Boolean;
  455.             saveEnvironment: QDEnvironment;
  456.     begin
  457.         pWE := hWE^;
  458.  
  459. { save the QuickDraw environment }
  460.         _WESaveQDEnvironment(pWE^.port, BTST(pWE^.flags, weFHasColorQD), saveEnvironment);
  461.  
  462. { make sure firstLine and lastLine are within the allowed range }
  463.         lineIndex := pWE^.nLines - 1;
  464.         firstLine := _WEPinInRange(firstLine, 0, lineIndex);
  465.         lastLine := _WEPinInRange(lastLine, 0, lineIndex);
  466.  
  467. { lock the line array }
  468.         saveLineLock := _WESetHandleLock(pWE^.hLines, true);
  469.         pLines := pWE^.hLines^;
  470.  
  471. { lock the text }
  472.         saveTextLock := _WESetHandleLock(pWE^.hText, true);
  473.         pText := LongInt(pWE^.hText^);
  474.  
  475. { find the style run index corresponding to the beginning of the first line }
  476.         runIndex := _WEOffsetToRun(pLines^[firstLine].lineStart, hWE);
  477.         previousRunIndex := -1;
  478.  
  479. { loop thru the specified lines }
  480.         for lineIndex := firstLine to lastLine do
  481.             begin
  482.  
  483. { get line start and line end }
  484.                 lineStart := pLines^[lineIndex].lineStart;
  485.                 lineEnd := pLines^[lineIndex + 1].lineStart;
  486.  
  487. { loop thru each style run on this line }
  488.                 repeat
  489.  
  490. { get style run information for the current style run }
  491.                     _WEGetIndStyle(runIndex, runInfo, hWE);
  492.  
  493.                     if (previousRunIndex <> runIndex) then
  494.                         begin
  495.  
  496. { set new text attributes }
  497.                             TextFont(runInfo.runAttrs.runStyle.tsFont);
  498.                             __TextFace(runInfo.runAttrs.runStyle.tsFace);
  499.                             TextSize(runInfo.runAttrs.runStyle.tsSize);
  500.  
  501. { remember previous run index }
  502.                             previousRunIndex := runIndex;
  503.                         end;
  504.  
  505. { determine the relative position of this style run on the line }
  506.                     styleRunPosition := 0;                                        { onlyStyleRun }
  507.  
  508.                     if (runInfo.runStart <= lineStart) then
  509.                         segmentStart := lineStart
  510.                     else
  511.                         begin
  512.                             styleRunPosition := styleRunPosition + 2;    { rightStyleRun or middleStyleRun }
  513.                             segmentStart := runInfo.runStart;
  514.                         end;
  515.  
  516.                     if (runInfo.runEnd >= lineEnd) then
  517.                         segmentEnd := lineEnd
  518.                     else
  519.                         begin
  520.                             styleRunPosition := styleRunPosition + 1;    { leftStyleRun or middleStyleRun }
  521.                             segmentEnd := runInfo.runEnd;
  522.                         end;
  523.  
  524. {$IFC WASTE_DEBUG}
  525. { our callback should never see an embedded object if the segment is empty }
  526.                     _WEAssert((segmentStart < segmentEnd) or (runInfo.runAttrs.runStyle.tsObject = kNullObject), 'Embedded Object in Empty Segment');
  527. {$ENDC}
  528.  
  529. { do the callback }
  530.                     if Callback(@pLines^[lineIndex], @runInfo.runAttrs, Ptr(pText + segmentStart), segmentStart, segmentEnd - segmentStart, styleRunPosition) then
  531.                         Leave;
  532.  
  533. { advance style run index, unless this style run goes on to the next line }
  534.                     if (runInfo.runEnd <= lineEnd) then
  535.                         runIndex := runIndex + 1;
  536.  
  537.                 until (runInfo.runEnd >= lineEnd);
  538.  
  539.             end;  { for }
  540.  
  541. { unlock the text }
  542.         if (_WESetHandleLock(pWE^.hText, saveTextLock)) then
  543.             ;
  544.  
  545. { unlock the line array }
  546.         if (_WESetHandleLock(pWE^.hLines, saveLineLock)) then
  547.             ;
  548.  
  549. { restore the QuickDraw environment }
  550.         _WERestoreQDEnvironment(saveEnvironment);
  551.  
  552.     end;  { _WESegmentLoop }
  553.  
  554. {$IFC WASTE_SEGMENT}
  555. {$S WASTE_TSM_SUPPORT}
  556. {$ENDC}
  557.  
  558.     procedure _WEDrawTSMHilite (var segmentRect: Rect;
  559.                                     tsFlags: SignedByte);
  560.         var
  561.             flags: LongInt;
  562.             underlineHeight: Integer;
  563.             background, foreground, saveForeground: RGBColor;
  564.             isColorPort: Boolean;
  565.             usingTrueGray: Boolean;
  566.     begin
  567.         flags := tsFlags;
  568.         isColorPort := CGrafPtr(GetQDGlobalsPtr^.thePort)^.portVersion < 0;
  569.         usingTrueGray := false;
  570.  
  571. { by default, the pen pattern is solid }
  572.         PenPat(GetQDGlobalsPtr^.black);
  573.  
  574. { if we're drawing in color, set the foreground color }
  575.         if (isColorPort) then
  576.             begin
  577.  
  578. { save foreground color }
  579.                 GetForeColor(saveForeground);
  580.  
  581. { by default, the foreground color is black }
  582.                 foreground.red := 0;
  583.                 foreground.green := 0;
  584.                 foreground.blue := 0;
  585.  
  586. { if we're underlining raw (unconverted) text, see if a "true gray" is available }
  587.                 if (not BTST(flags, tsTSMConverted)) then
  588.                     begin
  589.                         GetBackColor(background);
  590.                         usingTrueGray := GetGray(GetGDevice, background, foreground);
  591.                     end;  { if raw text }
  592.  
  593. { set the foreground color }
  594.                 RGBForeColor(foreground);
  595.  
  596.             end;  { if color graf port }
  597.  
  598. { if we're underlining raw (unconverted) text and no true gray is available, }
  599. { simulate gray with a 50% pattern }
  600.         if (not BTST(flags, tsTSMConverted)) then
  601.             if (usingTrueGray = false) then
  602.                 PenPat(GetQDGlobalsPtr^.gray);
  603.  
  604. { use a 2-pixel tall underline if text is "selected", else use a 1-pixel tall underline }
  605.         if BTST(flags, tsTSMSelected) then
  606.             underlineHeight := 2
  607.         else
  608.             underlineHeight := 1;
  609.  
  610. { segmentRect becomes the rectangle to paint }
  611.         InsetRect(segmentRect, 1, 0);
  612.         segmentRect.top := segmentRect.bottom - underlineHeight;
  613.  
  614. { draw the underline }
  615.         PaintRect(segmentRect);
  616.  
  617. { restore the foreground color }
  618.         if (isColorPort) then
  619.             RGBForeColor(saveForeground);
  620.  
  621.     end;  { _WEDrawTSMHilite }
  622.  
  623. {$IFC WASTE_SEGMENT}
  624. {$S}
  625. {$ENDC}
  626.  
  627.     procedure _WEDrawLines (firstLine, lastLine: LongInt;
  628.                                     doErase: Boolean;
  629.                                     hWE: WEHandle);
  630.  
  631. { draw the specified range of lines }
  632. { we can safely assume that the WE record is already locked }
  633. { and the port is already set the pWE^.port }
  634.  
  635.         var
  636.             pWE: WEPtr;
  637.             lineRect: Rect;                            { rectangle enclosing the current line }
  638.             drawRect: Rect;                        { visible portion of the line rectangle }
  639.             bounds: Rect;                            { bounds of the offscreen buffer, in global coordinates }
  640.             screenPort: GrafPtr;
  641.             screenDevice: GDHandle;
  642.             offscreenPixels: PixMapHandle;
  643.             usingColor: Boolean;                { TRUE if we're drawing in color }
  644.             usingOffscreen: Boolean;            { TRUE if we're using an offscreen port }
  645.             drawingOffscreen: Boolean;        { TRUE if actually drawing to an offscreen buffer }
  646.  
  647.         function SLDraw (pLine: LinePtr;
  648.                                         pAttrs: WERunAttributesPtr;
  649.                                         pSegment: Ptr;
  650.                                         segmentStart, segmentLength: LongInt;
  651.                                         styleRunPosition: JustStyleCode): Boolean;
  652.             var
  653.                 slop: Fixed;
  654.                 segmentRect: Rect;
  655.                 theColorBlack: RGBColor;
  656.         begin
  657.             SLDraw := false;                            { keep looping }
  658.  
  659. { is this the first segment on this line? }
  660.             if (styleRunPosition <= leftStyleRun) then
  661.                 begin
  662.  
  663. { calculate the line rectangle (the rectangle which completely encloses the current line) }
  664.                     lineRect.left := pWE^.destRect.left;
  665.                     lineRect.right := pWE^.destRect.right;
  666.                     lineRect.top := pWE^.destRect.top + pLine^.lineOrigin;
  667.                     lineRect.bottom := pWE^.destRect.top + LinePeek(pLine)^.second.lineOrigin;
  668.  
  669. { calculate the visible portion of this rectangle }
  670. { we do this by intersecting the line rectangle with the view rectangle }
  671.                     drawRect := pWE^.viewRgn^^.rgnBBox;
  672.                     if SectRect(lineRect, drawRect, drawRect) then
  673.                         ;
  674.  
  675.                     if (usingOffscreen) then
  676.                         begin
  677.  
  678. { calculate the boundary rectangle for the offscreen buffer }
  679. { this is simply drawRect converted to global coordinates }
  680.                             bounds := drawRect;
  681.                             LocalToGlobal(bounds.topLeft);
  682.                             LocalToGlobal(bounds.botRight);
  683.  
  684. { update the offscreen graphics world for the new bounds (this could fail) }
  685.                             drawingOffscreen := false;
  686.                             if (UpdateGWorld(GWorldPtr(pWE^.offscreenPort), 0, bounds, nil, nil, 0) >= 0) then
  687.                                 begin
  688.  
  689. { get the pixel map associated with the offscreen graphics world }
  690.  
  691. { NOTE: when running on a 68000 machine with the original QuickDraw, }
  692. { a GWorld is just an extended GrafPort, and GetGWorldPixMap actually }
  693. { returns a handle to a _copy_ of the GrafPort portBits (a BitMap, not a PixMap). }
  694. { An important side-effect of this is that when we call SetOrigin, }
  695. { only the original portBits is offset, not the copy. }
  696.  
  697.                                     offscreenPixels := GetGWorldPixMap(GWorldPtr(pWE^.offscreenPort));
  698.  
  699. { lock it down }
  700.                                     if LockPixels(offscreenPixels) then
  701.                                         begin
  702.  
  703. { offscreen pixel buffer allocation was successful }
  704.                                             drawingOffscreen := true;
  705.  
  706. { switch graphics world }
  707.                                             SetGWorld(GWorldPtr(pWE^.offscreenPort), nil);
  708.  
  709. { synchronize the coordinate system of the offscreen port with that of the screen port }
  710.                                             SetOrigin(drawRect.left, drawRect.top);
  711.  
  712. { reset the offscreen clip region }
  713.                                             ClipRect(drawRect);
  714.  
  715.                                         end;
  716.                                 end;  { if pixel buffer allocation was successful }
  717.                         end;  { if usingOffscreen }
  718.  
  719. { if doErase is TRUE, erase the drawable area before drawing text }
  720.                     if (doErase) then
  721.                         EraseRect(drawRect);
  722.  
  723. { position the pen }
  724.                     MoveTo(lineRect.left + _WECalcPenIndent(pLine^.lineSlop, pWE^.alignment), lineRect.top + pLine^.lineAscent);
  725.  
  726.                 end;  { if first segment on line }
  727.  
  728. { if drawingOffscreen, switch thePort to the offscreen port }
  729. { and synchronize text attributes }
  730.             if (drawingOffscreen) then
  731.                 begin
  732.                     SetPort(pWE^.offscreenPort);
  733.                     TextFont(pAttrs^.runStyle.tsFont);
  734.                     __TextFace(pAttrs^.runStyle.tsFace);
  735.                     TextSize(pAttrs^.runStyle.tsSize);
  736.                 end;  { if drawingOffscreen }
  737.  
  738. { get horizontal coordinate of the pen before drawing the segment }
  739.             GetPen(segmentRect.topLeft);
  740.  
  741. { set the foreground color }
  742.             if (usingColor) then
  743.                 RGBForeColor(pAttrs^.runStyle.tsColor);
  744.  
  745.             if (pAttrs^.runStyle.tsObject <> kNullObject) then
  746.                 begin
  747.  
  748. { EMBEDDED OBJECT }
  749.                     if (_WEDrawObject(WEObjectDescHandle(pAttrs^.runStyle.tsObject)) <> noErr) then
  750.                         ;        { don't know what to do with errors }
  751.  
  752.                 end
  753.             else
  754.                 begin
  755.  
  756. { REGULAR TEXT }
  757.  
  758.                     slop := 0;
  759.  
  760. { calculate the "slop" (extra space) for this text segment (justified text only) }
  761.                     if (pWE^.alignment = weJustify) then
  762.                         begin
  763.  
  764. { if this is the last segment on the line, strip trailing spaces }
  765.                             if (not Odd(styleRunPosition)) then
  766.                                 segmentLength := VisibleLength(pSegment, segmentLength);
  767.  
  768. { calculate how much extra space is to be applied to this text segment }
  769.                             slop := FixMul(PortionLine(pSegment, segmentLength, styleRunPosition, Point(kOneToOneScaling), Point(kOneToOneScaling)), pLine^.lineJustAmount);
  770.  
  771.                         end;  { if alignment = weJustify }
  772.  
  773. {$IFC WASTE_DEBUG}
  774.                     _WEAssert(pWE^.drawTextHook <> nil, 'Missing DrawText Hook');
  775. {$ENDC}
  776.  
  777. { draw the segment }
  778.                     CallWEDrawTextProc(pSegment, segmentLength, slop, styleRunPosition, hWE, pWE^.drawTextHook);
  779.  
  780.                 end;
  781.  
  782. { get horizontal coordinate of the pen after drawing the segment }
  783.             GetPen(segmentRect.botRight);
  784.             segmentRect.bottom := lineRect.bottom;
  785.  
  786. { if this segment is in the TSM area, underline it in the appropriate way }
  787.             if BTST(pAttrs^.runStyle.tsFlags, tsTSMHilite) then
  788.                 _WEDrawTSMHilite(segmentRect, pAttrs^.runStyle.tsFlags);
  789.  
  790.             if (drawingOffscreen) then
  791.                 begin
  792.                     if (not Odd(styleRunPosition)) then
  793.                         begin
  794.  
  795. { after drawing offscreen the last segment, }
  796. { prepare to copy the offscreen buffer to video RAM }
  797.  
  798. { first set the graphics world to the screen port }
  799.                             SetGWorld(GWorldPtr(screenPort), screenDevice);
  800.  
  801. { before calling CopyBits, set the foreground color to black to avoid colorization (color only) }
  802.                             if (usingColor) then
  803.                                 begin
  804.                                     theColorBlack.red := 0;
  805.                                     theColorBlack.green := 0;
  806.                                     theColorBlack.blue := 0;
  807.                                     RGBForeColor(theColorBlack);
  808.                                 end;
  809.  
  810. { copy the offscreen image of the [visible portion of the] line to the screen }
  811.                             CopyBits(pWE^.offscreenPort^.portBits, screenPort^.portBits, drawRect, drawRect, srcCopy, nil);
  812.  
  813. { restore the original offscreen coordinate system and unlock the pixel image }
  814.                             SetPort(pWE^.offscreenPort);
  815.                             SetOrigin(0, 0);
  816.                             if (usingColor) then
  817.                                 RGBForeColor(theColorBlack);            { this fixes a bug in Style 1.3 }
  818.                             UnlockPixels(offscreenPixels);
  819.  
  820.                         end;  { if last segment }
  821.  
  822. { restore the screen port for _WESegmentLoop }
  823.                     SetPort(screenPort);
  824.  
  825.                 end;  { if drawingOffscreen }
  826.         end;  { SLDraw }
  827.  
  828.     begin  { _WEDrawLines }
  829.         pWE := hWE^;
  830.         usingOffscreen := false;
  831.         drawingOffscreen := false;
  832.  
  833. { do nothing if our graphics port is not visible }
  834.         if EmptyRgn(pWE^.port^.visRgn) then
  835.             Exit(_WEDrawLines);
  836.  
  837. { save graphics world }
  838.         GetGWorld(GWorldPtr(screenPort), screenDevice);
  839.  
  840. { If doErase is TRUE, we're drawing over old text, so we must erase each line }
  841. { before redrawing it.  But if the weFDrawOffscreen feature is enabled, we draw }
  842. { the entire line offscreen and then we copy the image right over the old line, }
  843. { without erasing it, thus achieving a very smooth drawing effect. }
  844.  
  845.         if ((doErase) and BTST(pWE^.flags, weFDrawOffscreen)) then
  846.             begin
  847.  
  848. { has an offscreen world already been allocated? }
  849.                 if (pWE^.offscreenPort = nil) then
  850.                     begin
  851.  
  852. { nope, then create one; its bounds are set initially to an arbitrary rectangle }
  853.                         SetRect(bounds, 0, 0, 1, 1);
  854.                         if (NewGWorld(GWorldPtr(pWE^.offscreenPort), 0, bounds, nil, nil, pixPurge + noNewDevice + useTempMem) <> noErr) then
  855.                             ;
  856.                     end;
  857.  
  858.                 usingOffscreen := (pWE^.offscreenPort <> nil);
  859.             end;
  860.  
  861.         usingColor := BTST(pWE^.flags, weFHasColorQD);
  862.         _WESegmentLoop(firstLine, lastLine, SLDraw, hWE);
  863.  
  864. { restore graphics world }
  865.         SetGWorld(GWorldPtr(screenPort), screenDevice);
  866.  
  867.     end;  { _WEDrawLines }
  868.  
  869.     function _WECalcPenIndent (slop: Integer;
  870.                                     alignment: Integer): Integer;
  871.     begin
  872.  
  873. { if alignment is weFlushDefault, use the system global SysDirection }
  874.         if (alignment = weFlushDefault) then
  875.             if (GetSysDirection = 0) then
  876.                 alignment := weFlushLeft
  877.             else
  878.                 alignment := weFlushRight;
  879.  
  880.         if (alignment = weFlushRight) then
  881.             _WECalcPenIndent := slop                                { right aligned }
  882.         else if (alignment = weCenter) then
  883.             _WECalcPenIndent := slop div 2                        { centered }
  884.         else
  885.             _WECalcPenIndent := 0;                                    { left aligned or justified }
  886.     end;  { _WECalcPenIndent }
  887.  
  888.     procedure _WESaveQDEnvironment (port: GrafPtr;
  889.                                     saveColor: Boolean;
  890.                                     var theEnvironment: QDEnvironment);
  891.     begin
  892.         with theEnvironment do
  893.             begin
  894.                 GetPort(envPort);
  895.                 SetPort(port);
  896.                 GetPenState(envPen);
  897.                 PenNormal;
  898.                 envStyle.tsFont := port^.txFont;
  899.                 envStyle.tsFace := GrafPtr1(port)^.txFace;
  900.                 Boolean(envStyle.tsFlags) := saveColor;        { remember if color was saved }
  901.                 envStyle.tsSize := port^.txSize;
  902.                 if (saveColor) then
  903.                     GetForeColor(envStyle.tsColor);
  904.                 envMode := port^.txMode;
  905.                 TextMode(srcOr);
  906.             end;  { with }
  907.     end;  { _WESaveQDEnvironment }
  908.  
  909.     procedure _WERestoreQDEnvironment (var theEnvironment: QDEnvironment);
  910.     begin
  911.         with theEnvironment do
  912.             begin
  913.                 SetPenState(envPen);
  914.                 TextFont(envStyle.tsFont);
  915.                 __TextFace(envStyle.tsFace);
  916.                 TextSize(envStyle.tsSize);
  917.                 TextMode(envMode);
  918.                 if Boolean(envStyle.tsFlags) then
  919.                     RGBForeColor(envStyle.tsColor);
  920.                 SetPort(envPort);
  921.             end;  { with }
  922.     end;  { _WERestoreQDEnvironment }
  923.  
  924.     procedure _WEFillFontInfo (port: GrafPtr;
  925.                                     var targetStyle: WERunAttributes);
  926.  
  927. { given a WERunAttributes record, fill in the runHeight, runAscent fields etc. }
  928.         var
  929.             fInfo: FontInfo;
  930.             saveEnvironment: QDEnvironment;
  931.     begin
  932.         _WESaveQDEnvironment(port, false, saveEnvironment);
  933.         with targetStyle do
  934.             begin
  935.  
  936. { we don't want a zero font size; although QuickDraw accepts zero to mean }
  937. { the default font size, it can cause trouble to us when we do calculations }
  938.                 if (runStyle.tsSize = 0) then
  939.                     runStyle.tsSize := 12;
  940.  
  941. { set the text attributes }
  942.                 TextFont(runStyle.tsFont);
  943.                 TextSize(runStyle.tsSize);
  944.                 __TextFace(runStyle.tsFace);
  945.                 GetFontInfo(fInfo);
  946.                 runHeight := fInfo.ascent + fInfo.descent + fInfo.leading;
  947.                 runAscent := fInfo.ascent;
  948.             end;  { with }
  949.         _WERestoreQDEnvironment(saveEnvironment);
  950.     end;  { _WEFillFontInfo }
  951.  
  952.     procedure _WECopyStyle (var sourceStyle, targetStyle: WETextStyle;
  953.                                     offStyles: Integer;
  954.                                     mode: Integer);
  955.  
  956. { Copy some or all of the attributes composing sourceStyle to targetStyle. }
  957. { The mode parameter determines which attributes are to be copied and how. }
  958. { If mode contains weDoToggleFace, then offStyles indicates which }
  959. { QuickDraw styles are to be turned off. }
  960.  
  961.         var
  962.             longMode: LongInt;
  963.             longSize: LongInt;
  964.             sourceFace, targetFace: LongInt;
  965.  
  966.     begin
  967.         longMode := mode;    { this allows my compiler to generate tighter code }
  968.  
  969. { if the kModeFont bit is set, copy the font family number }
  970.         if BTST(longMode, kModeFont) then
  971.             begin
  972.                 targetStyle.tsFont := sourceStyle.tsFont;
  973.  
  974. {$IFC WASTE_RESOLVE_FONT_DESIGNATORS}
  975.                 if (targetStyle.tsFont = systemFont) then
  976.                     targetStyle.tsFont := GetSysFont;
  977.                 if (targetStyle.tsFont = applFont) then
  978.                     targetStyle.tsFont := GetAppFont;
  979. {$ENDC}
  980.             end;
  981.  
  982. { if the kModeSize or the kModeAddSize bit is set, alter the font size }
  983.         if (BAND(longMode, weDoSize + weDoAddSize) <> 0) then
  984.             begin
  985.  
  986. { copy size to a long variable to avoid integer overflows when doing additions }
  987.                 longSize := sourceStyle.tsSize;
  988.  
  989. { zero really means 12 }
  990.                 if (longSize = 0) then
  991.                     longSize := 12;
  992.  
  993. { if kModeAddSize is set, the source size is added to the target size, }
  994. { otherwise the source size replaces the target size outright }
  995.                 if BTST(longMode, kModeAddSize) then
  996.                     longSize := longSize + targetStyle.tsSize;
  997.  
  998. { range-check the resulting size }
  999.                 longSize := _WEPinInRange(longSize, kMinFontSize, kMaxFontSize);
  1000.                 targetStyle.tsSize := longSize;
  1001.  
  1002.             end;  { if alter size }
  1003.  
  1004. { if kModeFace is set, copy the QuickDraw styles (tsFace field); }
  1005. { the (rather complex) rules for copying the styles are explained below in detail }
  1006.         if BTST(longMode, kModeFace) then
  1007.             begin
  1008.                 sourceFace := sourceStyle.tsFace;
  1009.                 targetFace := targetStyle.tsFace;
  1010.  
  1011. { sourceFace replaces targetFace outright if one or both of these conditions hold: }
  1012. { 1. sourceFace is zero (= empty set = plain text) }
  1013. { 2. the kModeReplaceFace bit is set }
  1014.  
  1015.                 if ((sourceFace = 0) or BTST(longMode, kModeReplaceFace)) then
  1016.                     targetFace := sourceFace
  1017.                 else
  1018.                     begin
  1019.  
  1020. { Otherwise sourceFace is interpreted as a bitmap indicating }
  1021. { which styles are to be altered -- all other styles are left intact. }
  1022. { What exactly happens to the styles indicated in sourceFace }
  1023. { depends on whether the kModeToggleFace bit is set or clear. }
  1024.  
  1025. { if kModeToggleFace is set, turn a style off if it's set in offStyles, else turn it on }
  1026.                         if BTST(longMode, kModeToggleFace) then
  1027.                             targetFace := BOR(BXOR(sourceFace, offStyles), BAND(targetFace, BitNot(sourceFace)))
  1028.                         else
  1029.  
  1030. { if kModeToggleFace is clear, turn on the styles specified in sourceStyle }
  1031.                             targetFace := BOR(targetFace, sourceFace);
  1032.  
  1033. { the condense and extend attributes are mutually exclusive: if one is set }
  1034. { in sourceFace, remove it from targetFace }
  1035.                         if BTST(sourceFace, tsCondense) then
  1036.                             BCLR(targetFace, tsExtend)
  1037.                         else if BTST(sourceFace, tsExtend) then
  1038.                             BCLR(targetFace, tsCondense)
  1039.                     end;
  1040.  
  1041.                 targetStyle.tsFace := targetFace;
  1042.             end;  { if alter face }
  1043.  
  1044. { if kModeColor is set, change target color }
  1045.         if BTST(longMode, kModeColor) then
  1046.             targetStyle.tsColor := sourceStyle.tsColor;
  1047.  
  1048. { if kModeObject is set, copy object descriptor }
  1049.         if BTST(longMode, kModeObject) then
  1050.             targetStyle.tsObject := sourceStyle.tsObject;
  1051.  
  1052. { always clear targetStyle.tsFlags by default }
  1053.         targetStyle.tsFlags := 0;
  1054.  
  1055. { if kModeFlags is set, copy the tsFlags field }
  1056.         if BTST(longMode, kModeFlags) then
  1057.             targetStyle.tsFlags := sourceStyle.tsFlags;
  1058.  
  1059.     end;  { _WECopyStyle }
  1060.  
  1061.     function _WEOffsetInRange (offset: LongInt;
  1062.                                     edge: SignedByte;
  1063.                                     rangeStart, rangeEnd: LongInt): Boolean;
  1064.  
  1065. { return TRUE if the position specified by the pair < offset, edge > }
  1066. { is within the specified range }
  1067.  
  1068.     begin
  1069.  
  1070. { if edge is kTrailingEdge, offset really refers to the preceding character }
  1071.         if (edge = kTrailingEdge) then
  1072.             offset := offset - 1;
  1073.  
  1074. { return TRUE iff offset is within the specified range }
  1075.         _WEOffsetInRange := ((offset >= rangeStart) and (offset < rangeEnd));
  1076.  
  1077.     end;  { _WEOffsetInRange }
  1078.  
  1079. end.